import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as CANNON from 'cannon-es'
import { GUI } from 'lil-gui'

const gui = new GUI()

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight)
camera.position.set(0, 3, 3)

const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

const controls = new OrbitControls(camera, renderer.domElement)

const world = new CANNON.World()

world.broadphase = new CANNON.SAPBroadphase(world)

const defaultMaterial = new CANNON.Material('default')

const material = new CANNON.ContactMaterial(defaultMaterial, defaultMaterial, { friction: 0 })
world.defaultContactMaterial = material

world.gravity.set(0, -9.8, 0)

const axHelp = new THREE.AxesHelper(5)
scene.add(axHelp)

const meshes: THREE.Mesh[] = []
const bodyMeshes: CANNON.Body[] = []

const sphere = new THREE.Mesh(
  new THREE.SphereGeometry(0.5, 10, 10),
  new THREE.MeshBasicMaterial({ color: 'orange', wireframe: true })
)
meshes.push(sphere)
scene.add(sphere)


const sphereBody = new CANNON.Body({
  mass: 1, shape: new CANNON.Sphere(0.5), position: new CANNON.Vec3(0, 3, 0),
})
bodyMeshes.push(sphereBody)
world.addBody(sphereBody)

const plane = new THREE.Mesh(
  new THREE.BoxGeometry(4, 0.1, 4),
  new THREE.MeshBasicMaterial({ color: 'grey' })
)
scene.add(plane)


const planeBody = new CANNON.Body({
  shape: new CANNON.Box(new CANNON.Vec3(2, 0.05, 2))
})
bodyMeshes.push(planeBody)
world.addBody(planeBody)

const debugParams = {
  force() {
    sphereBody.applyForce(new CANNON.Vec3(20, 0, 0), new CANNON.Vec3(0, 0, 0))
  }
}

gui.add(debugParams, 'force')


const clock = new THREE.Clock()

const handleRender = () => {
  world.step(1 / 60, clock.getDelta(), 3)
  for(let i = 0; i < meshes.length; i++) {
    meshes[i].position.copy(bodyMeshes[i].position as any)
    meshes[i].quaternion.copy(bodyMeshes[i].quaternion as any)
  }
  controls.update()
  renderer.render(scene, camera)
  window.requestAnimationFrame(handleRender)
}

handleRender()

const handleSize = () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()

  renderer.setSize(window.innerWidth, window.innerHeight)
}

window.addEventListener('resize', handleSize)